package com.yuan.util.web;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.*;
import com.yuan.util.string.StrUtil;
import com.yuan.util.string.URLEncoderUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * @author YouYuan
 * @version 1.0
 * @Email xiyousuiyuan@163.com
 * @Date 2017/12/31 15:59
 */
public class WebUtil {

    public static final String FORMAT_JSON = "json";
    public static final String FORMAT_JSONP = "jsonp";

    public static String CHARSET = "UTF-8";
    public static String dateFormat = "yyyy-MM-dd HH:mm:ss";

    public static SerializeConfig serializeConfig = new SerializeConfig();

    public static class ToStringSerializer implements ObjectSerializer {
        public static final ToStringSerializer instance = new ToStringSerializer();
        @Override
        public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType,
                          int features) throws IOException {
            SerializeWriter out = serializer.getWriter();
            if (object == null) {
                out.writeNull();
                return;
            }
            out.writeString(object.toString());
        }
    }

    static {
        SimpleDateFormatSerializer dateFormatSerializer = new SimpleDateFormatSerializer(dateFormat);
        ToStringSerializer stringSerializer = new ToStringSerializer();
        serializeConfig.put(java.util.Date.class, dateFormatSerializer);
        serializeConfig.put(java.sql.Timestamp.class, dateFormatSerializer);
        serializeConfig.put(java.sql.Date.class, dateFormatSerializer);
        serializeConfig.put(java.lang.Long.class, stringSerializer);
        serializeConfig.put(java.lang.Double.class, stringSerializer);
    }

    /**
     * 获取request所有请求参数
     * @param request
     * @return
     */
    public static Map getParamMap(HttpServletRequest request) {
        return getParamMap(request, Boolean.FALSE, null);
    }

    /**
     * 获取request所有请求参数，按照给定编码格式进行URLDecoder解码
     * @param request
     * @param charset
     * @return
     */
    public static Map getParamMap(HttpServletRequest request, String charset) {
        return getParamMap(request, Boolean.TRUE, charset);
    }

    private static Map getParamMap(HttpServletRequest request, boolean isEncode, String charset) {
        Map paramMap = new HashMap();
        if (request == null) {
            return paramMap;
        }
        Enumeration names = request.getParameterNames();
        while (names.hasMoreElements()) {
            String paramName = (String) names.nextElement();
            String paramValue = request.getParameter(paramName);
            if (isEncode && URLEncoderUtil.isEncoded(paramValue)) {
                try {
                    paramValue = URLDecoder.decode(paramValue, charset == null ? CHARSET : charset);
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            }
            paramMap.put(paramName, paramValue);
        }
        return paramMap;
    }

    /**
     * response输出指定的数据
     * @param request
     * @param response
     * @param resultMap 输出数据的Map
     * @param close 是否自动关闭Response输出流，关闭后将不能再次输出
     * @throws IOException
     */
    public static void outPrint(HttpServletRequest request, HttpServletResponse response, Map resultMap, boolean close) throws IOException {
        if (response == null) {
            throw new RuntimeException("异常！response为null");
        }
        PrintWriter out = getPrintWriter(response);
        String printData = getPrintData(request, resultMap);
        out.print(printData);//默认输出json格式
        if (close) {
            out.close();
        }
    }

    /**
     * response输出指定的数据，输出完毕后自动关闭response输出流
     * @param request
     * @param response
     * @param resultMap
     * @throws IOException
     */
    public static void outPrint(HttpServletRequest request, HttpServletResponse response, Map resultMap) throws IOException {
        outPrint(request, response, resultMap, Boolean.TRUE);
    }

    public static String getPrintData(HttpServletRequest request, Map resultMap) throws IOException {
        String format = StrUtil.trim(request.getParameter("_format"), WebUtil.FORMAT_JSON);
        String callback = StrUtil.trim(request.getParameter("_callback"));
        String jsonStr = JSON.toJSONString(resultMap, serializeConfig, new SerializerFeature[]{SerializerFeature.WriteMapNullValue});
        if (WebUtil.FORMAT_JSONP.equals(format)) {
            //输出jsonp格式，支持跨域
            StringBuilder rs = new StringBuilder();
            rs.append(callback).append("(").append(jsonStr).append(")");
            return rs.toString();
        }
        return jsonStr;//默认输出json格式
    }

    /**
     * 获取response的PrintWriter对象，并自动设置编码（默认UTF-8）
     * @param response
     * @return
     * @throws IOException
     */
    public static PrintWriter getPrintWriter(HttpServletResponse response) throws IOException {
        if (response != null) {
            response.setCharacterEncoding(CHARSET);
            response.setContentType("text/html;charset=" + CHARSET);
            response.setHeader("cache-control", "no-cache");
            return response.getWriter();
        }
        return null;
    }

    /**
     * 获取用户真实IP地址
     * 只能尽可能的获取到真实ip，但不能保证一定可以获取到真实ip，而且代理服务器请求头中获取的ip是可伪造的。
     *
     * @param request
     * @return
     */
    public static String getIpAddress(HttpServletRequest request) {
        String ip = null;
        //X-Forwarded-For：Squid 服务代理
        String ipAddresses = request.getHeader("X-Forwarded-For");
        if (StrUtil.isEmpty(ipAddresses) || "unknown".equalsIgnoreCase(ipAddresses)) {
            //Proxy-Client-IP：apache 服务代理
            ipAddresses = request.getHeader("Proxy-Client-IP");
        }
        if (StrUtil.isEmpty(ipAddresses) || "unknown".equalsIgnoreCase(ipAddresses)) {
            //WL-Proxy-Client-IP：weblogic 服务代理
            ipAddresses = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StrUtil.isEmpty(ipAddresses) || "unknown".equalsIgnoreCase(ipAddresses)) {
            //HTTP_CLIENT_IP：有些代理服务器
            ipAddresses = request.getHeader("HTTP_CLIENT_IP");
        }
        if (StrUtil.isEmpty(ipAddresses) || "unknown".equalsIgnoreCase(ipAddresses)) {
            //X-Real-IP：nginx服务代理
            ipAddresses = request.getHeader("X-Real-IP");
        }
        if (StrUtil.isEmpty(ipAddresses) || "unknown".equalsIgnoreCase(ipAddresses)) {
            ipAddresses = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        //有些网络通过多层代理，那么获取到的ip就会有多个，一般都是通过逗号（,）分割开来，并且第一个ip为客户端的真实IP
        if (StrUtil.notEmpty(ipAddresses) && !"unknown".equalsIgnoreCase(ipAddresses)) {
            ip = ipAddresses.split(",")[0];
        } else {
            //还是不能获取到，最后再通过request.getRemoteAddr();获取
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    /**
     * 判断request请求是否是json格式
     * @param request
     * @return
     */
    public static boolean isJsonContent(HttpServletRequest request) {
        String contentTypeHeader = request.getHeader("Content-Type");
        String acceptHeader = request.getHeader("Accept");
        String xRequestedWith = request.getHeader("X-Requested-With");
        return StrUtil.contain(contentTypeHeader, "application/json")
                || StrUtil.contain(acceptHeader, "application/json")
                || StrUtil.equalsIgnoreCase("XMLHttpRequest", xRequestedWith);
    }

    public static String getCHARSET() {
        return CHARSET;
    }

    /**
     * 设置编码格式
     * @param CHARSET
     */
    public static void setCHARSET(String CHARSET) {
        WebUtil.CHARSET = CHARSET;
    }
}
